// Copyright 2010 The Walk Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"log"
	"time"

	"github.com/lxn/walk"
	. "github.com/lxn/walk/declarative"
)

const (
	B_NIL     = 0 //0空白
	B_S       = 1 //1身
	B_T       = 2 //2头
	B_W       = 3 //3尾
	B_C       = 4 //4食物
	DENSITY   = 1 //密度 像素 移动偏移量
	BODY_SIZE = 5 //身体大小
)

//存放 x y坐标
type loct struct {
	x, y int
	b    int //0空白 1身 2头 3尾 4食物
}

//身属性
type bodyAttr struct {
	rgb   walk.Color    //身颜色
	style walk.PenStyle //身样式
	width int           //身宽度
}

type MyMainWindow struct {
	*walk.MainWindow
	paintWidget *walk.CustomWidget
	canvas      *walk.Canvas
	canvasXY    [][]loct   //画布的所有坐标点
	density     int        //密度 倍数 身宽
	onOff       bool       //开关
	isAddTail   bool       //是否添加尾
	isCalculate bool       //是否正在计算
	body        []loct     //身位置
	food        []loct     //食物位置
	randomStart loct       //随机和起始 x y坐标
	borderX     loct       //边界X
	borderY     loct       //边界Y
	mouseXY     loct       //鼠标坐标
	bodyAttr    bodyAttr   //身样属性
	background  walk.Color //背景颜色
	headSize    loct       //头大小

}

//初始化参数
func (mw *MyMainWindow) init() {
	//密度 像素 移动偏移量
	mw.density = DENSITY

	//随机起始 x y坐标
	mw.randomStart = loct{x: 100, y: 100}

	//边界
	mw.borderX = loct{x: 1, y: 799}
	mw.borderY = loct{x: 1, y: 599}

	//初始化所有坐标点
	mw.canvasXY = make([][]loct, 0, mw.borderX.y)
	for x := 0; x < mw.borderX.y; x++ {
		yloct := make([]loct, 0, mw.borderY.y)
		for y := 0; y < mw.borderY.y; y++ {
			yloct = append(yloct, loct{x: x, y: y})
		}
		mw.canvasXY = append(mw.canvasXY, yloct)
	}
	log.Println(len(mw.canvasXY), len(mw.canvasXY[0]))

	//鼠标坐标
	mw.mouseXY = loct{x: 800, y: 100}

	//食物位置
	mw.food = make([]loct, 0, 1)

	//背景色
	mw.background = walk.RGB(240, 240, 240)
	//头大小
	mw.headSize = loct{x: 10, y: 10}

	//身位置
	mw.body = make([]loct, 0, 1)
	//设置头位置 默认为 ,随机起始 x y坐标
	mw.body = append(mw.body, loct{x: mw.randomStart.x, y: mw.randomStart.y})
	var head loct
	for i := 0; i < 25; i++ {
		head = mw.head() //获取头位置
		mw.randomStart.x = head.x + mw.density
		mw.appendBody()
	}
	mw.body[0].b = B_W
	log.Println("初始化参数结束", mw.len())
}

//头向目标方向移动一次
func (mw *MyMainWindow) appendBody() {
	//判断是否到了边界
	if mw.isBorder(mw.randomStart.x, mw.randomStart.y) {
		mw.body[mw.len()-1].b = B_S
		mw.body = append(mw.body, loct{
			x: mw.randomStart.x,
			y: mw.randomStart.y,
			b: B_T,
		})
		//标记为头
		mw.canvasXY[mw.randomStart.x][mw.randomStart.y].b = B_T
	} else {
		log.Println(mw.randomStart.x, mw.randomStart.y, "到达边界")
		mw.stop()
		//提示
		walk.MsgBox(mw, "提示", "结束!", walk.MsgBoxIconInformation)
		return
	}
}

//去除尾
func (mw *MyMainWindow) removeTail() {
	tail := mw.tail()
	if tail.b == B_W {
		mw.bodyAttr = bodyAttr{rgb: walk.RGB(240, 240, 240), style: walk.PenInsideFrame, width: BODY_SIZE}
		tail0 := mw.bodyLoct(0)
		form := walk.Point{tail0.x, tail0.y}
		tail1 := mw.bodyLoct(1)
		to := walk.Point{tail1.x, tail1.y}
		mw.drawBody(form, to)
		//将去除的标记为空白
		mw.canvasXY[tail.x][tail.y].b = B_NIL
		//去除尾
		mw.body = mw.body[1:]
		//标记为尾部
		mw.body[0].b = B_W
		//标记为尾部
		mw.canvasXY[mw.body[0].x][mw.body[0].y].b = B_W
	}
}

//移动速度(刷新频率)
func (mw *MyMainWindow) moveSpeed() {
	sleep(time.Second / 2)
	s := time.Second / 5
	log.Println(s, "刷新一次")
	for {
		//s 秒循环一次
		//默认向鼠标坐标移动
		//每次循环根据鼠标坐标先计算移动方向,给随机起始坐标赋新计算直
		sleep(s)
		if mw.onOff && !mw.isAddTail && !mw.isCalculate {

			// 获取头和尾
			// 头向目标方向移动一次
			// 身重新画到画布上 方式一 方式二
			// 移动之后去除尾部
			beforHead := mw.head() //之前的头
			//根据鼠标坐标先计算移动方向
			mw.calcMoveDirection()

			//重新画头 移除头
			//mw.removeHead()

			form := walk.Point{mw.body[mw.len()-3].x, mw.body[mw.len()-3].y}
			to := walk.Point{mw.body[mw.len()-2].x, mw.body[mw.len()-2].y}
			mw.bodyAttr = bodyAttr{rgb: walk.RGB(0, 0, 0), style: walk.PenSolid, width: BODY_SIZE}
			mw.drawBody(form, to)

			// 头向目标方向加一次
			mw.appendBody()

			//身重新画到画布上
			//方式一 循环每个坐标都重新画
			//			for i := 0; i < mw.len()-1; i++ {
			//				form := walk.Point{mw.body[i].x, mw.body[i].y}
			//				to := walk.Point{mw.body[i+1].x, mw.body[i+1].y}
			//				mw.drawBody(form, to)
			//			}
			//方式二 只画头位置
			form = walk.Point{beforHead.x, beforHead.y}
			to = walk.Point{mw.body[mw.len()-1].x, mw.body[mw.len()-1].y}
			mw.bodyAttr = bodyAttr{rgb: walk.RGB(0, 0, 0), style: walk.PenSolid, width: BODY_SIZE}
			mw.drawBody(form, to)

			//将头线画成头
			//mw.draeHead()

			//去除尾部
			mw.removeTail()
		}
	}
}

//计算移动方向
func (mw *MyMainWindow) calcMoveDirection() {
	mw.isCalculate = true
	r := GeneralEquation(mw.head(), mw.mouseXY)
	//if r.x != 0 && r.y != 0 && AngleEquation(mw.head(), mw.mouseXY, 180) < 45 {
	if r.x != 0 && r.y != 0 {
		mw.randomStart = r
		//log.Println(r, mw.head(), mw.mouseXY, AngleEquation(mw.head(), mw.mouseXY))
	} else {
		var x = mw.randomStart.x
		var y = mw.randomStart.y
		if mw.mouseXY.x > mw.head().x {
			x += DENSITY
		} else if mw.mouseXY.x < mw.head().x {
			x -= DENSITY
		}
		if mw.mouseXY.y > mw.head().y {
			y += DENSITY
		} else if mw.mouseXY.y < mw.head().y {
			y -= DENSITY
		}
		mw.randomStart.x = x
		mw.randomStart.y = y
		//mw.movinDirection()
	}
	mw.isCalculate = false
}

//移动方向
func (mw *MyMainWindow) movinDirection() {
	head := mw.head()
	mouse := mw.mouseXY
	ae := AngleEquation(head, mouse, 360)
	log.Println(head, mouse, ae)
}

//画身
func (mw *MyMainWindow) drawBody(form, to walk.Point) error {
	linesBrush, err := walk.NewSolidColorBrush(mw.bodyAttr.rgb)
	if err != nil {
		return err
	}
	defer linesBrush.Dispose()
	linesPen, err := walk.NewGeometricPen(mw.bodyAttr.style, mw.bodyAttr.width, linesBrush)
	if err != nil {
		return err
	}
	defer linesPen.Dispose()
	if err := mw.canvas.DrawLine(linesPen, form, to); err != nil {
		return err
	}
	return nil
}

//画头
func (mw *MyMainWindow) draeHead() error {
	headBrush, err := walk.NewSolidColorBrush(mw.bodyAttr.rgb)
	if err != nil {
		return err
	}
	defer headBrush.Dispose()
	w := mw.headSize.x
	h := mw.headSize.y
	if err := mw.canvas.FillEllipse(headBrush, walk.Rectangle{mw.head().x - w/2, mw.head().y - h/2, w, h}); err != nil {
		return err
	}
	return nil
}

//移除头
func (mw *MyMainWindow) removeHead() error {
	//先将头位置换成背景色
	headBrush, err := walk.NewSolidColorBrush(mw.background)
	if err != nil {
		return err
	}
	defer headBrush.Dispose()
	w := mw.headSize.x
	h := mw.headSize.y
	if err := mw.canvas.FillEllipse(headBrush, walk.Rectangle{mw.head().x - w/2, mw.head().y - h/2, w, h}); err != nil {
		return err
	}

	return nil
}

//添加尾
func (mw *MyMainWindow) addTail() {
	mw.isAddTail = true
	newTail := []loct{mw.body[0]}
	mw.body = append(newTail, mw.body...)
	mw.isAddTail = false
}

//事件和创建画布
func (mw *MyMainWindow) regEvent() {
	//鼠标移动事件
	mw.paintWidget.MouseMove().Attach(func(x, y int, button walk.MouseButton) {
		if mw.isBorder(x, y) {
			if x != 0 && y != 0 {
				//当前鼠标坐标
				mw.mouseXY = loct{x: x, y: y}
			}
			//头位置
			//head := mw.head()
			//log.Println(head.x, head.y)
		} else {
			log.Println("边界 ", "x :", x, " y :", y)
		}
	})
	//测试添加尾
	mw.paintWidget.MouseDown().Attach(func(x, y int, button walk.MouseButton) {
		mw.addTail()
	})
}

//判断是否到边界
func (mw *MyMainWindow) isBorder(x, y int) bool {
	v1 := x >= mw.borderX.x && x <= mw.borderX.y && y >= mw.borderY.x
	v2 := x >= mw.borderX.x && y >= mw.borderY.x && y <= mw.borderY.y
	return v1 && v2
}

//获取 头 位置
func (mw *MyMainWindow) head() loct {
	return mw.bodyLoct(mw.len() - 1)
}

//获取 尾 位置
func (mw *MyMainWindow) tail() loct {
	return mw.bodyLoct(0)
}

//获取身指定位置
func (mw *MyMainWindow) bodyLoct(index int) loct {
	return mw.body[index]
}

//画布初始化
func (mw *MyMainWindow) canvasInit(c *walk.Canvas, updateBounds walk.Rectangle) error {
	canvas, err := mw.paintWidget.CreateCanvas()
	if err != nil {
		log.Fatal(err)
		return err
	}
	mw.canvas = canvas
	//画背景
	mw.drawBackground()
	//身初始化
	mw.bodyInit()
	return nil
}

//画背景
func (mw *MyMainWindow) drawBackground() {
	bounds := mw.paintWidget.ClientBounds()
	//画背景
	backgroundBrush, err := walk.NewSolidColorBrush(mw.background)
	if err != nil {
		return
	}
	defer backgroundBrush.Dispose()
	if err := mw.canvas.FillRectangle(backgroundBrush, bounds); err != nil {
		log.Fatal(err)
		return
	}
	//画线条
	//	ellipseBrush, err := walk.NewHatchBrush(walk.RGB(0, 255, 0), walk.HatchCross)
	//	if err != nil {
	//		log.Fatal(err)
	//		return
	//	}
	//	defer ellipseBrush.Dispose()
	//	if err := mw.canvas.FillRectangle(ellipseBrush, bounds); err != nil {
	//		log.Fatal(err)
	//		return
	//	}
}

//开始
func (mw *MyMainWindow) start() {
	mw.onOff = true
}

//停止
func (mw *MyMainWindow) stop() {
	mw.onOff = false
}

//获取身长度
func (mw *MyMainWindow) len() int {
	return len(mw.body)
}

//休眠
func sleep(sleep time.Duration) {
	time.Sleep(sleep)
}

//身初始化
func (mw *MyMainWindow) bodyInit() {
	form := walk.Point{mw.body[0].x, mw.body[0].y}
	to := walk.Point{mw.body[mw.len()-1].x, mw.body[mw.len()-1].y}
	mw.bodyAttr = bodyAttr{rgb: walk.RGB(0, 0, 0), style: walk.PenSolid, width: BODY_SIZE}
	mw.drawBody(form, to)
	mw.removeHead()
	mw.draeHead()
}

//主函数
func main() {
	log.Println("main run")
	mw := new(MyMainWindow)

	dmw := MainWindow{
		AssignTo:  &mw.MainWindow,
		Title:     "学习 by 杨 爱 张",
		MinSize:   Size{320, 240},
		Size:      Size{807, 629},
		Layout:    VBox{MarginsZero: true},
		Maximize:  true,
		Minimize:  true,
		FixedSize: true,
		Children: []Widget{
			CustomWidget{
				AssignTo:            &mw.paintWidget,
				ClearsBackground:    true,
				InvalidatesOnResize: true,
				Paint:               mw.canvasInit,
			},
			//PushButton{},
		},
	}
	dmw.Create()

	if ic, err := walk.NewIconFromFile("icon.ico"); err != nil {
		return
	} else {
		mw.MainWindow.SetIcon(ic)
	}
	//初始化参数
	mw.init()
	//注册事件
	mw.regEvent()
	//开始
	mw.start()
	//移动速度(刷新频率)
	go mw.moveSpeed()

	run := mw.MainWindow.Run()
	fmt.Println("exit :", run)
}
